/*
 *  Note.cpp
 *  SoundController0002
 *
 *  Created by jiro on 11/06/18.
 *  Copyright 2011 jiro music. All rights reserved.
 *
 */


#include "Note.h"
#include <sstream>

namespace sc
{

	Note::Interval Note::GetInterval( Note::Id note0, Note::Id note1 )
	{
		int n = note1 - note0;
		while ( n >= 12 )
		{
			n -= 12;
		}
		while ( n < 0 )
		{
			n += 12;
		}
		return static_cast< Note::Interval >( n );
	}

	Note::Id Note::GetNoteIdFrom( MidiNumber number )
	{
		if ( number == MidiNumberMute )
		{
			return Mute;
		}
		Note::Id id = Id( static_cast< int >( number + 3 ) % 12 );
		return id;
	}
	
	int Note::GetOctaveFrom( MidiNumber number ) 
	{
		if ( number == MidiNumberMute )
		{
			return 0;
		}
//		std::cout << static_cast< double >( number ) / 12.0 - 6.0 << std::endl;
		return std::floor( static_cast< double >( number + 3 ) / 12.0 - 6.0 );
	}

	std::string Note::ToString( Id noteId ) 
	{
		switch ( noteId ) {
		case Note::A:
			return "A";
		case Note::BFlat:
			return "Bb";
		case Note::B:
			return "B";
		case Note::C:
			return "C";
		case Note::DFlat:
			return "Db";
		case Note::D:
			return "D";
		case Note::EFlat:
			return "Eb";
		case Note::E:
			return "E";
		case Note::F:
			return "F";
		case Note::GFlat:
			return "Gb";
		case Note::G:
			return "G";
		case Note::AFlat:
			return "Ab";
		default:
			return "";
		}
	}
	
	std::string Note::ToString( MidiNumber midiNumber )
	{
		std::stringstream ss;
		int divisor = midiNumber / 12;
		int remainder = midiNumber % 12;  
		switch ( remainder )
		{
		case 0:
			ss << "C";
			break;
		case 1:
			ss << "C#";
			break;
		case 2:
			ss << "D";
			break;
		case 3:
			ss << "D#";
			break;
		case 4:
			ss << "E";
			break;
		case 5:
			ss << "F";
			break;
		case 6:
			ss << "F#";
			break;
		case 7:
			ss << "G";		
			break;
		case 8:
			ss << "G#";
			break;
		case 9:
			ss << "A";
			break;
		case 10:
			ss << "A#";
			break;
		case 11:
			ss << "B";
			break;
		}
		ss << divisor - 1;
		return ss.str();
	}

	std::string Note::ToString( Interval pitch )
	{
		bool isMinus = false;
		if ( pitch < 0 )
		{
			pitch = static_cast< Interval>( -1 * pitch );
			isMinus = true;
		}
		std::string sign = ( isMinus ? std::string( "-" ) : std::string( "" ) );
		switch ( pitch ) {
		case sc::Note::Perfect1st:

			return sign + "Perfect1st";

		case Minor2nd:
			return sign + "Minor2nd";

		case Major2nd:
			return sign + "Major2nd";

		case Minor3rd:
			return sign + "Minor3rd";

		case Major3rd:
			return sign + "Major3rd";

		case Perfect4th:
			return sign + "Perfect4th";

		case Augment4th:
			return sign + "Augment4th";

		case Perfect5th:
			return sign + "Perfect5th";

		case Minor6th:
			return sign + "Minor6th";

		case Major6th:
			return sign + "Major6th";

		case Minor7th:
			return sign + "Minor7th";

		case Major7th:
			return sign + "Major7th";

		default:
			return "";
		}
	}	
	
	std::string Note::IntervalToString( int pitchByChromaticCount, bool omissionExpression )
	{
		bool isMinuns = false;
		if ( pitchByChromaticCount < 0 )
		{
			pitchByChromaticCount *=-1;
			isMinuns = true;
		}

		switch ( pitchByChromaticCount ) {
		case sc::Note::Perfect1st:
			return ( isMinuns ? std::string( "-" ) : std::string( "" ) ) + 
				( omissionExpression ? std::string( "Per1st" ) : std::string( "Perfect1st" ) );

		case Minor2nd:
			return ( isMinuns ? std::string( "-" ) : std::string( "" ) ) +
				( omissionExpression ? std::string( "Min2nd" ) : std::string( "Minor2nd" ) );

		case Major2nd:
			return ( isMinuns ? std::string( "-" ) : std::string( "" ) ) + 
				( omissionExpression ? std::string( "Maj2nd" ) : std::string( "Maj2nd" ) );

		case Minor3rd:
			return ( isMinuns ? std::string( "-" ) : std::string( "" ) ) + 
				( omissionExpression ? std::string( "Min3rd" ) : std::string( "Minor3rd" ) );

		case Major3rd:
			return ( isMinuns ? std::string( "-" ) : std::string( "" ) ) +
				( omissionExpression ? std::string( "Maj3rd" ) : std::string( "Major3rd" ) );

		case Perfect4th:
			return ( isMinuns ? std::string( "-" ) : std::string( "" ) ) +
				( omissionExpression ? std::string( "Per4th" ) : std::string( "Perfect4th" ) );

		case Augment4th:
			return ( isMinuns ? std::string( "-" ) : std::string( "" ) ) +
				( omissionExpression ? std::string( "Aug4th" ) : std::string( "Augment4th" ) );

		case Perfect5th:
			return ( isMinuns ? std::string( "-" ) : std::string( "" ) ) +
				( omissionExpression ? std::string( "Per5th" ) : std::string( "Perfect5th" ) );

		case Minor6th:
			return ( isMinuns ? std::string( "-" ) : std::string( "" ) ) +
				( omissionExpression ? std::string( "Min6th" ) : std::string( "Minor6th" ) );

		case Major6th:
			return ( isMinuns ? std::string( "-" ) : std::string( "" ) ) +
				( omissionExpression ? std::string( "Maj6th" ) : std::string( "Major6th" ) );

		case Minor7th:
			return ( isMinuns ? std::string( "-" ) : std::string( "" ) ) +
				( omissionExpression ? std::string( "Min7th" ) : std::string( "Minor7th" ) );

		case Major7th:
			return ( isMinuns ? std::string( "-" ) : std::string( "" ) ) +
				( omissionExpression ? std::string( "Maj7th" ) : std::string( "Major7th" ) );

		default:
			return "";
		}
	}

	template< class Container >
	std::string Note::ToString( const Container& chord )
	{
		std::stringstream sstr;
		sstr << "( ";
//		copy( chord.begin(), chord.end(), std::ostream_iterator< std::string >( sstr, ", " ) );
		for ( typename Container::const_iterator iter = chord.begin(); iter != chord.end(); iter++ )
		{
			sstr << ToString( *iter ) << ", ";
		}
		sstr << ")";
		return sstr.str();
	}
	
}
